﻿using Avalon.Core.Data;
using Avalon.Core.Data.Templates;
using Avalon.Core.MVC;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Avalon.Core.Providers {
    /// <summary>
    /// The renderer provider. This provider looks up the different view renderers in the application
    /// and returs the right RendererDescriptor.
    /// </summary>
    public class RendererProvider : BaseProvider, IRendererProvider {
        /// <summary>
        /// The list of view renderers
        /// </summary>
        private static List<AvalonViewRendererGroup> ViewRendererGroups { get; set; }

        /// <summary>
        /// CTOR
        /// </summary>
        public RendererProvider() {
            if (ViewRendererGroups == null) initialize();
            _rendererDescriptor = null;
        }

        /// <summary>
        /// Reload the view renderer groups from the database.
        /// </summary>
        public new void Reload() {
            _rendererDescriptor = null;
            initialize();
        }

        private void initialize() {
            //The view renderer groups are defined in the database. Each group contains view renderers used
            //to render models and view models.
            ViewRendererGroups = RepositoryConfig.Initialize<AvalonViewRendererGroup>().Get().ToList();
        }

        private static IRendererDescriptor _rendererDescriptor;

        public IRendererDescriptor GetViewRenderer() {
            if (_rendererDescriptor != null) return _rendererDescriptor;

            //Get the view renderer group by the controller name.
            AvalonViewRendererGroup group = ViewRendererGroups.FirstOrDefault(vrg => vrg.Name == Controller);
            //If this group is null return the default view renderer group.
            if (group == null) {
                group = ViewRendererGroups.FirstOrDefault(vrg => vrg.Name == "Default");
            }
            //If there is still no group halt execution and throw new Exeption
            if (group == null) throw new InvalidOperationException("There should always be a view renderer group. Application cannot execute,");

            //If the group is not null return the right view renderer
            AvalonViewRenderer viewRenderer = group.AvalonViewRenderers.FirstOrDefault(vr => vr.Action == this.Action);
            if (viewRenderer == null) throw new ArgumentNullException("The view renderer is null.");
            _rendererDescriptor = new RendererDescriptor() {
                ViewRenderer = new AvalonRenderer(viewRenderer),
                Template = viewRenderer.Template,
                DefaultTemplate = group.DefaultTemplate,
                GetPipeline = viewRenderer.GetPipeline,
                PostPipeline = viewRenderer.PostPipeline
            };

            //return
            return _rendererDescriptor;
        }

        public IRendererDescriptor GetViewRenderer(Type rendererType) {
            //If only the rendererType is supplied and this rendererType is a IViewRenderHelper, return the renderer
            if (typeof(IViewRenderHelper).IsAssignableFrom(rendererType)) {
                return new RendererDescriptor() {
                    ViewRenderer = new AvalonRenderer() { ViewRenderHelper = (IViewRenderHelper)Activator.CreateInstance(rendererType) }
                };
            }
            else {
                //Get the view renderer group by the controller name.
                AvalonViewRendererGroup group = ViewRendererGroups.FirstOrDefault(vrg => vrg.Name == Controller);
                //If this group is null return the default view renderer group.
                if (group == null) {
                    group = ViewRendererGroups.FirstOrDefault(vrg => vrg.Name == "Default");
                }
                //If there is still no group halt execution and throw new Exeption
                if (group == null) throw new InvalidOperationException("There should always be a view renderer group. Application cannot execute,");

                AvalonViewRenderer viewRenderer = group.AvalonViewRenderers.FirstOrDefault(vr => vr.Renderer == rendererType.FullName);
                if (viewRenderer == null) throw new ArgumentNullException("The view renderer is null.");
                RendererDescriptor rendererDescriptor = new RendererDescriptor() {
                    ViewRenderer = new AvalonRenderer(viewRenderer),
                    Template = viewRenderer.Template,
                    DefaultTemplate = group.DefaultTemplate,
                    GetPipeline = viewRenderer.GetPipeline,
                    PostPipeline = viewRenderer.PostPipeline
                };

                //return
                return rendererDescriptor;
            }
        }

        public IRendererDescriptor GetViewRenderer(string viewName, string folderName) {
            return new RendererDescriptor() {
                ViewName = viewName,
                FolderName = folderName
            };
        }
    }
}
